package com.yh.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import io.minio.*;
import org.apache.log4j.Logger;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * 文件的上传和下载；如果上传文件到本地，但本地地址不存在，将直接导致上传失败。
 *
 * @author yuhuan
 * @OtherSource 配置源。读取外部资源
 * @MultipartFile 待上传的文件
 * @MinioClient minio服务器客户端
 * @date 2022/01/13
 */
public class UploadAndDownload {
    /**
     * 日志
     */
    private static final Logger logger = Logger.getLogger(UploadAndDownload.class);

    /**
     * 通过'MultipartFile'覆盖式的将文件上传到本地磁盘，上传地址不存在则终止上传
     */
    public static boolean uploadToDisk(String uploadPath, MultipartFile multipartFile) {
        boolean exist = isExist(uploadPath);
        if (!exist) {
            logger.debug(">>>>> 上传到磁盘失败！原因是上传地址不存在");
            return false;
        }
        Path path = Paths.get(uploadPath, multipartFile.getOriginalFilename());
        try {
            multipartFile.transferTo(path);
        } catch (IOException e) {
            logger.debug(">>>>> 上传到磁盘失败！原因是出现 IOException");
            return false;
        }
        return true;
    }

    /**
     * 使用'Hutool'工具覆盖式的将文件写入到nginx的本地（在linux环境中使用），上传地址不存在则终止上传
     */
    public static boolean uploadToNginxLocalhost(String nginxPath, MultipartFile file, String bucketName) {
        boolean exist = isExist(ImgUrl.spliceNginxBasicFileUrl(nginxPath, bucketName));
        if (!exist) {
            logger.debug(">>>>> 上传到nginx本地失败！原因是上传地址不存在");
            return false;
        }
        // 拼接nginx服务器的本地上传地址
        String path = nginxPath + ImgUrl.spliceBasicUrl(bucketName, file.getOriginalFilename());
        try {
            // 覆盖式的的上传
            FileUtil.writeBytes(file.getBytes(), path);
        } catch (IOException e) {
            logger.debug(">>>>> 上传到nginx本地失败！原因是出现 IOException");
            return false;
        }
        return true;
    }

    /**
     * 上传文件到服务器（MinIO），如果桶名不存在则创建桶继续上传
     */
    public static boolean uploadToMinIo(MinioClient client, String fileUrlName, MultipartFile headFile) {
        // 获取文件的原名
        String imgName = headFile.getOriginalFilename();
        // 获取文件的类型，尽量不使用"application/octet-stream"作为文件类型
        String contentType = headFile.getContentType();
        if (ObjectUtil.isEmpty(contentType)) {
            contentType = getMimeType(imgName);
        }
        try {
            // 获取文件流
            InputStream inputStream = headFile.getInputStream();
            // 编辑储存桶的名称参数
            BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(fileUrlName).build();
            // 判断储存桶是否已经存在
            boolean exists = client.bucketExists(bucketExistsArgs);
            if (!exists) {
                // 创建储存桶
                client.makeBucket(MakeBucketArgs.builder().bucket(fileUrlName).build());
            }
            /** 上传文件到储存桶
             * 注意，使用 application/octet-stream 上传时, 浏览器打开链接会默认进行下载而不是在浏览器中浏览该文件。所以，在
             * 上传时前，先使用 file.getContentType() 得到文件的类型，然后再根据文件类型进行上传。这样通过返回的 URL 就可以
             * 直接预览该文件
             * @bucket 设置储存桶的名称
             * @object 设置文件名称
             * @stream 设置待上传的文件
             * @contentType 设置文件的类型*/
            client.putObject(PutObjectArgs.builder()
                    .bucket(fileUrlName)
                    .object(imgName)
                    .stream(inputStream, headFile.getSize(), -1)
                    .contentType(contentType)
                    .build());
            inputStream.close();
        } catch (Exception e) {
            logger.error(">>>>> 上传到MinIO服务器失败！");
            return false;
        }
        return true;
    }

    /**
     * 根据文件名称解析媒体类型
     */
    private static String getMimeType(String fileName) {
        // 从文件名猜测其媒体类型，如果猜测失败就设置为通用的二进制文件
        String mimeType = URLConnection.guessContentTypeFromName(fileName);
        if (mimeType == null) {
            mimeType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
        }
        return mimeType;
    }

    /**
     * 判断目录是否存在
     */
    private static boolean isExist(String uploadPath) {
        if (ObjectUtil.isEmpty(uploadPath)) {
            return false;
        }
        // 获取抽象路径
        File file = new File(uploadPath);
        return file.exists();
    }
}
